/**
 * 
 */
package langnstats.project.lib;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Map;

import langnstats.project.lib.crossvalidation.TrainTokens;
/**
 * @author qing
 *
 */
public abstract class ExternalLanguageModel implements LanguageModel {
	
	private Process process;
	private String commandLine;
	private String tempFile; 
	private BufferedReader tempReader= null;
	private String lmFile = null;
	/**
	 * 
	 */
	private static final long serialVersionUID = 909589711531901448L;

	/* (non-Javadoc)
	 * @see langnstats.project.lib.LanguageModel#getDescription()
	 */
	public String getDescription() {
		return "External_LM";
	}

	/* (non-Javadoc)
	 * @see langnstats.project.lib.LanguageModel#predict(langnstats.project.lib.WordType)
	 */
	public double[] predict(WordType wordType) {
		Map<String,Double> r = parseNextWord();
		double[] dbl = new double[WordType.size()];
		double sum = 0;
		for(int i = 0; i < WordType.size() ; i++){
			dbl[i] = r.get(WordType.values()[i]);
			sum+= dbl[i];
		}
		// Normalize
		for(int i =0;i<WordType.size();i++){
			dbl[i] /= sum;
		}
		return dbl;
	}
	
	/**
	 * Calculate all necessary probability and write to temp file 
	 * @param allWordType
	 */
	public abstract void prepare(WordType[] allWordType);
	
	/* (non-Javadoc)
	 * @see langnstats.project.lib.LanguageModel#train(langnstats.project.lib.crossvalidation.TrainTokens)
	 */
	public void train(TrainTokens trainTokens) {
		PrintWriter wr = null;
		try {
			wr = new PrintWriter(new FileWriter(tempFile));
		} catch (IOException e) {
			System.err.println("Error, training failed due to IO Exception on Temp File: " + tempFile);
			e.printStackTrace();
			return ;
		}
		WordType[] wt = trainTokens.getTokenArray();
		for(int i = 0 ; i<wt.length ; i++){
			String str = wt[i].getOriginalTag();
			convertNextWord(str,wr);
		}
		wr.close();
		Runtime runtime = Runtime.getRuntime();
		String actualCommand = "";
		lmFile = tempFile+".lm";
		try {
			actualCommand = commandLine +" train " + tempFile + " " + lmFile ;
			PrintWriter cmdwr = new PrintWriter(new FileWriter("/tmp/exec"));
			cmdwr.print(commandLine);
			cmdwr.close();
 			process = runtime.exec("bash /tmp/exec");
            BufferedReader stdInput = new BufferedReader(new 
                    InputStreamReader(process.getInputStream()));

            BufferedReader stdError = new BufferedReader(new 
                    InputStreamReader(process.getErrorStream()));
 			
 			while(true){
 				try{
 	 				if(process.exitValue()==0){
 	 					System.err.println("Training Successfully Completed");
 	 					break;
 	 	 			}else{
 	 	 				System.err.println("Training FAILED!");
 	 					break;
 	 	 			}
 	 			}catch(IllegalThreadStateException ie){
 	 				Thread.sleep(100);
 	 			} 				
 			}
		} catch (IOException e) {
			System.err.println("Training Failed! The command line: " + actualCommand);
			e.printStackTrace();
		} catch (InterruptedException e) {
			System.err.println("Training program terminated! The command line: " + actualCommand);
			e.printStackTrace();
		}
		File f = new File(lmFile);
		if(f.exists()){
			System.err.println("Training OK, outputed LM: " + lmFile);
		}else{
			System.err.println("Training Failed, expected LM file: " + lmFile + " does not exist");
			lmFile=null;
			return;
		}
	}
	
	public LanguageModel clone(){
		return null;
	}

	public Process getProcess() {
		return process;
	}

	/**
	 * Get the prediction of all the possible tokens
	 * @return
	 */
	public abstract Map<String,Double> parseNextWord();
	
	/**
	 * Convert next word
	 * @return
	 */
	public abstract void convertNextWord(String str, PrintWriter wr);
	
	public String getCommandLine() {
		return commandLine;
	}
	
	/**
	 * Create the external lm process by using command line. It is responsible for starting the process
	 * and call "process" to read/write from the process's IO 
	 * @param commandLine
	 * @throws IOException
	 * @throws InterruptedException 
	 */
	public ExternalLanguageModel(String commandLine,String tempFile) throws IOException, InterruptedException{
		this.commandLine = commandLine;
		this.tempFile = tempFile;
	}

	public BufferedReader getTempReader() {
		return tempReader;
	}

	public String getLmFile() {
		return lmFile;
	}

	public void setLmFile(String lmFile) {
		this.lmFile = lmFile;
	}

	public String getTempFile() {
		return tempFile;
	}

	public void setTempFile(String tempFile) {
		this.tempFile = tempFile;
	}

}
